<?php
namespace juven\libs {
    \defined( '_JEXEC' ) or die( 'Restricted access' );
    
    class DatabaseAdapter {
        
        /** @var PDO */
        protected $_pdo = \NULL;

        /** @var array */
        protected $_vars = array();

        public function __construct(){}

        /**
         * @param DynamicContainer $dbConnectionData
         * @return PDO Returns PDO object
         */
        public function connect(DynamicContainer $dbConnectionData){
            $this->_vars['dbData'] = $dbConnectionData;
            if (!\is_null($this->_pdo)){ $this->disconnect(); }
            
            $dtp = new \stdClass(); 
            $dtp->vars = &$this->_vars; 
            $dtp->pdo  = &$this->_pdo;
            
            juven::eventsManager()->publish(self::EVENT_onBefore_ConnectToDatabase(), $dtp);
                $this->_pdo = new \PDO($this->_vars['dbData']->type . ':host=' . $this->_vars['dbData']->host . ';dbname=' . $this->_vars['dbData']->name,
                                       $this->_vars['dbData']->username, $this->_vars['dbData']->user_password, array(\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
                $this->_pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
                $this->_pdo->exec("SET CHARACTER SET utf8");
            juven::eventsManager()->publish(self::EVENT_onAfter_ConnectToDatabase(), $dtp);
            return $this->_pdo;
        }
        
        /** disconnect */
        public function disconnect(){ $this->_pdo = \NULL; }

        /** @return DynamicContainer */
        public function get_dbConnectionData(){  return $this->_vars['dbData'];  }

        /**
         * select
         * @param string $sqlQuery An SQL query string
         * @param array $bindParams Paramters to bind
         * @param constant $fetchMode A PDO Fetch mode
         * @return array Returns an assoc array
         */
        public function select($sqlQuery, array $bindParams = array(), $fetchMode = \PDO::FETCH_ASSOC){
            $dtp = new \stdClass();
            $dtp->sqlQuery = $sqlQuery; 
            $dtp->bindParams = $bindParams; 
            $dtp->fetchMode = $fetchMode;
            
            juven::eventsManager()->publish(self::EVENT_onBefore_QuerySelect(), $dtp);
                $sth = $this->_pdo->prepare($dtp->sqlQuery);
                foreach ($dtp->bindParams as $key => $value) { $sth->bindValue("$key", $value); }
                $dtp->selectResults = $sth->execute();
                $dtp->selectResultsData = $sth->fetchAll($dtp->fetchMode);
            juven::eventsManager()->publish(self::EVENT_onAfter_QuerySelect(), $dtp);
            return $dtp->selectResultsData;
        }

        /**
         * insert
         * @param string $tableName A name of table to insert into
         * @param array $dataToInsert An associative array
         * @return integer Returns the id of the inserted row
         */
        public function insert($tableName, array $dataToInsert) {
            $dtp = new \stdClass();
            $dtp->tableName = $tableName; 
            $dtp->dataToInsert = $dataToInsert;
            
            \ksort($dtp->dataToInsert);
            $fieldNames = \implode('`, `', \array_keys($dtp->dataToInsert));
            $fieldValues = ':' . \implode(', :', \array_keys($dtp->dataToInsert));
            $dtp->sqlQuery = "INSERT INTO `".$dtp->tableName."` (`$fieldNames`) VALUES ($fieldValues)";

            juven::eventsManager()->publish(self::EVENT_onBefore_QueryInsert(), $dtp);
                $sth = $this->_pdo->prepare($dtp->sqlQuery);
                foreach ($dtp->dataToInsert as $key => $value) { $sth->bindValue(":$key", $value); }
                $dtp->insertResults = $sth->execute();
                $dtp->lastInsertID  = $this->_pdo->lastInsertId();
            juven::eventsManager()->publish(self::EVENT_onAfter_QueryInsert(), $dtp);
            return $dtp->lastInsertID;
        }

        /**
         * update
         * @param string $tableName A name of table to insert into
         * @param array $dataToUpdate An associative array
         * @param string $where the WHERE query part
         * @return integer Returns the number of affected rows
         */
        public function update($tableName, array $dataToUpdate, $where) {
            $dtp = new \stdClass();
            $dtp->tableName = $tableName; $dtp->dataToUpdate = $dataToUpdate; $dtp->where = $where;
            
            \ksort($dtp->dataToUpdate); $fieldDetails = "";
            foreach ($dtp->dataToUpdate as $key => $value) { $fieldDetails .= "`$key`=:$key,"; } $fieldDetails = \rtrim($fieldDetails, ',');
            $dtp->sqlQuery = "UPDATE `".$dtp->tableName."` SET $fieldDetails WHERE ".$dtp->where;

            juven::eventsManager()->publish(self::EVENT_onBefore_QueryUpdate(), $dtp);
                $sth = $this->_pdo->prepare($dtp->sqlQuery);
                foreach ($dtp->dataToUpdate as $key => $value) { $sth->bindValue(":$key", $value); }
                $dtp->updateResults = $sth->execute();
            juven::eventsManager()->publish(self::EVENT_onAfter_QueryUpdate(), $dtp);
            return $dtp->updateResults;
        }

        /**
         * delete
         * 
         * @param string $tableName
         * @param string $where
         * @param integer $limit
         * @return integer the number of affected rows
         */
        public function delete($tableName, $where, $limit = 1) {
            $dtp = new \stdClass();
            $dtp->tableName = $tableName; $dtp->where = $where; $dtp->limit = $limit;
            $dtp->sqlQuery = "DELETE FROM `".$dtp->tableName."` WHERE ".$dtp->where." LIMIT ".$dtp->limit;

            juven::eventsManager()->publish(self::EVENT_onBefore_QueryDelete(), $dtp);
                $dtp->deleteResults = $this->_pdo->exec($dtp->sqlQuery);
            juven::eventsManager()->publish(self::EVENT_onAfter_QueryDelete(), $dtp);
            return $dtp->deleteResults;
        }

        /**
         * delete_all
         * @param string $tableName
         * @param string $where
         * @return integer the number of affected rows
         */
        public function delete_all($tableName, $where) {
            $dtp = new \stdClass();
            $dtp->tableName = $tableName; $dtp->where = $where;
            $dtp->sqlQuery = "DELETE FROM `".$dtp->tableName."` WHERE ".$dtp->where;

            juven::eventsManager()->publish(self::EVENT_onBefore_QueryDeleteAll(), $dtp);
                $dtp->deleteAll_Results = $this->_pdo->exec($dtp->sqlQuery);
            juven::eventsManager()->publish(self::EVENT_onAfter_QueryDeleteAll(), $dtp);
            return $dtp->deleteAll_Results;
        }
    
        public static function EVENT_onBefore_ConnectToDatabase(){  return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onBefore_QuerySelect(){        return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onBefore_QueryInsert(){        return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onBefore_QueryUpdate(){        return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onBefore_QueryDelete(){        return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onBefore_QueryDeleteAll(){     return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_ConnectToDatabase(){   return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_QuerySelect(){         return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_QueryInsert(){         return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_QueryUpdate(){         return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_QueryDelete(){         return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_QueryDeleteAll(){      return __CLASS__."\\".__FUNCTION__; }
    }
}